﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Utility.PoolSystem;

namespace Utility.DataSystem
{
	public interface IFieldSource
	{
		string Name { get; }
		DataValue GetData (IDataFieldContext context, FieldQuery fieldQuery, out bool isInterested);
	}

	public class FieldSources : IDisposable, IPoolCallback
	{
		static SimplePool<FieldSources> Pool { get; } = new SimplePool<FieldSources> (() => new FieldSources ());
		static HashSet<IFieldSource> RemoveSet { get; } = new HashSet<IFieldSource> ();

		public static FieldSources New () => Pool.Spawn ();

		public void Dispose ()
		{
			Pool.Despawn (this);
		}

		void IPoolCallback.OnSpawn ()
		{
			set.Clear ();
		}

		void IPoolCallback.OnDespawn ()
		{
			set.Clear ();
		}

		FieldSources () { }
		HashSet<IFieldSource> set = new HashSet<IFieldSource> ();

		public bool IsEmpty => set.Count == 0;

		public void Add (IFieldSource source)
		{
			set.Add (source);
		}

		public void Add (IEnumerable<IFieldSource> sources)
		{
			set.AddRange (sources);
		}

		public DataValue GetDataAndFilter (IDataFieldContext context, FieldQuery fieldQuery)
		{
			var msg = FieldCenter.GetDebugMsg (fieldQuery);

			RemoveSet.Clear ();
			bool interested;

			DataValue data = default;
			foreach (var source in set)
			{
				if (source != null)
				{
					msg?.StartSource (source);
					var _data = source.GetData (context, fieldQuery, out interested);
					data = fieldQuery.Setting.CombineValue (data, _data);
					if (!interested)
					{
						RemoveSet.Add (source);
					}
					msg?.EndSource (source, _data, !interested);
				}
			}
			//filter
			RemoveSet.Add (null);
			set.ExceptWith (RemoveSet);
			RemoveSet.Clear ();
			return data;
		}

		public DataValue GetData (IDataFieldContext context, FieldQuery fieldQuery)
		{
			var msg = FieldCenter.GetDebugMsg (fieldQuery);

			DataValue data = default;
			foreach (var source in set)
			{
				if (source != null)
				{
					msg?.StartSource (source);
					var _data = source.GetData (context, fieldQuery, out _);
					data = fieldQuery.Setting.CombineValue (data, _data);
					msg?.EndSource (source, _data);
				}
			}
			return data;
		}
	}

	public struct FieldQuery
	{
		public FieldQuery (DataFieldSetting setting, IDataFieldContext sourceContext, IList<DataValue> parameters, int targetEntities = 0)
		{
			Setting = setting;
			SourceContext = sourceContext;
			Parameters = parameters;
			TargetEntities = targetEntities;
			RefreshValue = false;
		}
		public FieldQuery (DataFieldSetting setting, IDataFieldContext sourceContext, int targetEntities = 0) : this (setting, sourceContext, null, targetEntities)
		{
		}

		public DataFieldSetting Setting { get; }
		public IDataFieldContext SourceContext { get; }
		public int TargetEntities { get; }
		public IList<DataValue> Parameters { get; }
		public bool RefreshValue { get; set; }

		public bool HasParam => (Parameters?.Count ?? 0) > 0;
	}
}